﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using SimpleSpreadsheet.Core;
using SimpleSpreadsheet.Core.Expressions;

namespace SimpleSpreadsheet.Functions
{
    /// <summary>
    /// Evaluates the reduce expression
    /// </summary>
    [Export(typeof (IFunction))]
    public class Reduce : IFunction
    {
        private readonly List<decimal> _cellValues = new List<decimal>();
        private decimal _cumulativeValue;
        private decimal _currentValue;
        private ExpressionVisitor _expressionVisitor;
        private BaseExpression _reduceExpression;

        #region IFunction Members

        public string Name
        {
            get { return "REDUCE"; }
        }

        public decimal Evaluate(IList<BaseExpression> arguments, ExpressionVisitor visitor)
        {
            if (arguments.Count < 3)
                throw new ArgumentException(String.Format("Wrong number of arguments in {0} function", Name));

            _expressionVisitor = visitor;
            _expressionVisitor.EvaluateParameter += expressionVisitor_EvaluateParameter;

            // Initial value of Y
            BaseExpression initialValueExpression = arguments[arguments.Count - 2];
            decimal initialValue = Convert.ToDecimal(_expressionVisitor.Evaluate(initialValueExpression));

            // Reduce expression containing parameters X and Y
            _reduceExpression = arguments[arguments.Count - 1];

            // Fill cell values list from input cell range
            for (int i = 0; i < arguments.Count - 2; i++)
            {
                _cellValues.Add(Convert.ToDecimal(_expressionVisitor.Evaluate(arguments[i])));
            }

            // Set the initial value
            _cumulativeValue = initialValue;

            // Perform the reduce evaluation
            while (_cellValues.Count > 0)
            {
                _currentValue = _cellValues[0];
                _cumulativeValue = Convert.ToDecimal(_expressionVisitor.Evaluate(_reduceExpression));
                _cellValues.RemoveAt(0);
            }
            _expressionVisitor.EvaluateParameter -= expressionVisitor_EvaluateParameter;
            return _cumulativeValue;
        }

        #endregion

        /// <summary>
        /// Get the value of X (cumulative) or Y (current) parameter in REDUCE expression
        /// </summary>
        /// <param name="name">parameter name</param>
        /// <param name="args"></param>
        private void expressionVisitor_EvaluateParameter(string name, ParameterArgs args)
        {
            if (name == "X")
                args.Result = _cumulativeValue;

            if (name == "Y")
                args.Result = _currentValue;
        }
    }
}